import '../helpers.dart';
import '../yt_service_provider.dart';
import 'utils.dart';

mixin LibraryMixin on YTMusicServices {
  Future<Map> getLibrarySongs({String? continuationParams}) async {
    Map<String, dynamic> body = {'browseId': 'FEmusic_liked_videos'};

    final response = await sendRequest('browse', body,
        additionalParams: continuationParams ?? '');

    Map outerContents = {};
    if (continuationParams != null) {
      outerContents =
          nav(response, ['continuationContents', 'musicShelfContinuation']);
    } else {
      Map contents = nav(response, [
        'contents',
        'singleColumnBrowseResultsRenderer',
        'tabs',
        0,
        'tabRenderer',
        'content',
        'sectionListRenderer',
        'contents',
        0,
      ]);
      outerContents = nav(contents, ['musicShelfRenderer']) ??
          nav(contents, ['itemSectionRenderer']);
    }

    String? continuation = nav(outerContents,
        ['continuations', 0, 'nextContinuationData', 'continuation']);
    if (continuation != null) {
      continuation = getContinuationString(continuation);
    }
    List contents = nav(outerContents, ['contents']);

    return {
      'contents': handleContents(contents),
      'continuation': continuation,
    };
  }

  Future<Map> getLibraryAlbums({String? continuationParams}) async {
    Map<String, dynamic> body = {'browseId': 'FEmusic_liked_albums'};
    final response = await sendRequest('browse', body);
    Map outerContents = {};
    if (continuationParams != null) {
      outerContents =
          nav(response, ['continuationContents', 'gridContinuation']);
    } else {
      Map contents = nav(response, [
        'contents',
        'singleColumnBrowseResultsRenderer',
        'tabs',
        0,
        'tabRenderer',
        'content',
        'sectionListRenderer',
        'contents',
        0,
      ]);
      outerContents = nav(contents, [
            'gridRenderer',
          ]) ??
          nav(contents, [
            'itemSectionRenderer',
          ]);
    }
    String? continuation = nav(outerContents,
        ['continuations', 0, 'nextContinuationData', 'continuation']);
    if (continuation != null) {
      continuation = getContinuationString(continuation);
    }
    List contents =
        nav(outerContents, ['items']) ?? nav(outerContents, ['contents']);

    return {
      'contents': handleContents(contents),
      'continuation': continuation,
    };
  }

  Future<Map> getLibraryArtists({String? continuationParams}) async {
    Map<String, dynamic> body = {
      'browseId': 'FEmusic_library_corpus_track_artists'
    };

    final response = await sendRequest('browse', body,
        additionalParams: continuationParams ?? '');
    Map outerContents = {};
    if (continuationParams != null) {
      outerContents =
          nav(response, ['continuationContents', 'musicShelfContinuation']);
    } else {
      Map contents = nav(response, [
        'contents',
        'singleColumnBrowseResultsRenderer',
        'tabs',
        0,
        'tabRenderer',
        'content',
        'sectionListRenderer',
        'contents',
        0,
      ]);
      outerContents = nav(contents, [
            'musicShelfRenderer',
          ]) ??
          nav(contents, [
            'itemSectionRenderer',
          ]);
    }
    String? continuation = nav(outerContents,
        ['continuations', 0, 'nextContinuationData', 'continuation']);
    if (continuation != null) {
      continuation = getContinuationString(continuation);
    }
    List contents = nav(outerContents, ['contents']);
    return {
      'contents': handleContents(contents),
      'continuation': continuation,
    };
  }

  Future<Map> getLibraryPlaylists(
      {int limit = 25, String? continuationParams}) async {
    Map<String, dynamic> body = {'browseId': 'FEmusic_liked_playlists'};
    var response = await sendRequest('browse', body,
        additionalParams: continuationParams ?? '');
    Map outerContents = {};
    if (continuationParams != null) {
      outerContents =
          nav(response, ['continuationContents', 'gridContinuation']);
    } else {
      Map contents = nav(response, [
        'contents',
        'singleColumnBrowseResultsRenderer',
        'tabs',
        0,
        'tabRenderer',
        'content',
        'sectionListRenderer',
        'contents',
        0,
      ]);
      outerContents = nav(contents, ['gridRenderer']) ??
          nav(contents, [
            'itemSectionRenderer',
          ]);
    }
    String? continuation = nav(outerContents,
        ['continuations', 0, 'nextContinuationData', 'continuation']);
    if (continuation != null) {
      continuation = getContinuationString(continuation);
    }
    List contents = nav(outerContents, ['items']);
    if (continuationParams == null) {
      contents = contents.sublist(1);
    }

    return {
      'contents': handleContents(contents),
      'continuation': continuation,
    };
  }

  Future<Map> getLibrarySubscriptions({String? continuationParams}) async {
    Map<String, dynamic> body = {'browseId': 'FEmusic_library_corpus_artists'};
    final response = await sendRequest('browse', body,
        additionalParams: continuationParams ?? '');
    Map outerContents = {};
    if (continuationParams != null) {
      outerContents = outerContents =
          nav(response, ['continuationContents', 'musicShelfContinuation']);
    } else {
      Map contents = nav(response, [
        'contents',
        'singleColumnBrowseResultsRenderer',
        'tabs',
        0,
        'tabRenderer',
        'content',
        'sectionListRenderer',
        'contents',
        0,
      ]);
      outerContents = nav(contents, [
            'musicShelfRenderer',
          ]) ??
          nav(contents, [
            'itemSectionRenderer',
          ]);
    }
    String? continuation = nav(outerContents,
        ['continuations', 0, 'nextContinuationData', 'continuation']);
    if (continuation != null) {
      continuation = getContinuationString(continuation);
    }
    List contents = nav(outerContents, ['contents']);

    return {
      'contents': handleContents(contents),
      'continuation': continuation,
    };
  }

  Future<Map<String, dynamic>> importPlaylist(String browseId) async {
    Map<String, dynamic> body = {'browseId': browseId};
    var response = await sendRequest('browse', body);
    Map<String, dynamic> header = nav(response, [
      'contents',
      'twoColumnBrowseResultsRenderer',
      'tabs',
      0,
      'tabRenderer',
      'content',
      'sectionListRenderer',
      'contents',
      0,
      'musicResponsiveHeaderRenderer'
    ]);
    Map<String, dynamic> result = handlePageHeader(header);
    if (result['endpoint'] == null) {
      result['endpoint'] = {"browseId": browseId};
    }
    return result;
  }

  Future<List> getHistory() async {
    Map<String, dynamic> body = {'browseId': 'FEmusic_history'};
    var response = await sendRequest('browse', body);

    List allContents = nav(response, [
      'contents',
      'singleColumnBrowseResultsRenderer',
      'tabs',
      0,
      'tabRenderer',
      'content',
      'sectionListRenderer',
      'contents',
    ]);
    List result = [];
    for (var content in allContents) {
      List contents = nav(content, ['musicShelfRenderer', 'contents']);
      Map header = nav(content, ['musicShelfRenderer']);
      Map section = {
        'title': nav(header, ['title', 'runs', 0, 'text']),
        'contents': handleContents(contents),
      };
      result.add(section);
    }
    return result;
  }

  // Future<bool> removeHistoryItem(List feedbackTokens) async {
  //   if (!isLogged.value) return false;
  //   Map<String, dynamic> body = {'feedbackTokens': feedbackTokens};
  //   final response = await sendRequest('feedback', body);
  //   return nav(response, ['feedbackResponses', 0, 'isProcessed']) == true;
  // }

//     def get_library_songs(self,
//                           limit: int = 25,
//                           validate_responses: bool = False,
//                           order: str = None) -> List[Dict]:
//         """
//         Gets the songs in the user's library (liked videos are not included).
//         To get liked songs and videos, use :py:func:`get_liked_songs`

//         :param limit: Number of songs to retrieve
//         :param validate_responses: Flag indicating if responses from YTM should be validated and retried in case
//             when some songs are missing. Default: False
//         :param order: Order of songs to return. Allowed values: 'a_to_z', 'z_to_a', 'recently_added'. Default: Default order.
//         :return: List of songs. Same format as :py:func:`get_playlist`
//         """
//         self._check_auth()
//         body = {'browseId': 'FEmusic_liked_videos'}
//         validate_order_parameter(order)
//         if order is not None:
//             body["params"] = prepare_order_params(order)
//         endpoint = 'browse'
//         per_page = 25

//         request_func = lambda additionalParams: self._send_request(endpoint, body)
//         parse_func = lambda raw_response: parse_library_songs(raw_response)

//         if validate_responses and limit is None:
//             raise Exception("Validation is not supported without a limit parameter.")

//         if validate_responses:
//             validate_func = lambda parsed: validate_response(parsed, per_page, limit, 0)
//             response = resend_request_until_parsed_response_is_valid(request_func, None,
//                                                                      parse_func, validate_func, 3)
//         else:
//             response = parse_func(request_func(None))

//         results = response['results']
//         songs = response['parsed']
//         if songs is None:
//             return []

//         if 'continuations' in results:
//             request_continuations_func = lambda additionalParams: self._send_request(
//                 endpoint, body, additionalParams)
//             parse_continuations_func = lambda contents: parse_playlist_items(contents)

//             if validate_responses:
//                 songs.extend(
//                     get_validated_continuations(results, 'musicShelfContinuation',
//                                                 limit - len(songs), per_page,
//                                                 request_continuations_func,
//                                                 parse_continuations_func))
//             else:
//                 remaining_limit = None if limit is None else (limit - len(songs))
//                 songs.extend(
//                     get_continuations(results, 'musicShelfContinuation', remaining_limit,
//                                       request_continuations_func, parse_continuations_func))

//         return songs

//     def get_library_albums(self, limit: int = 25, order: str = None) -> List[Dict]:
//         """
//         Gets the albums in the user's library.

//         :param limit: Number of albums to return
//         :param order: Order of albums to return. Allowed values: 'a_to_z', 'z_to_a', 'recently_added'. Default: Default order.
//         :return: List of albums.

//         Each item is in the following format::

//             {
//               "browseId": "MPREb_G8AiyN7RvFg",
//               "playlistId": "OLAK5uy_lKgoGvlrWhX0EIPavQUXxyPed8Cj38AWc",
//               "title": "Beautiful",
//               "type": "Album",
//               "thumbnails": [...],
//               "artists": [{
//                 "name": "Project 46",
//                 "id": "UCXFv36m62USAN5rnVct9B4g"
//               }],
//               "year": "2015"
//             }
//         """
//         self._check_auth()
//         body = {'browseId': 'FEmusic_liked_albums'}
//         validate_order_parameter(order)
//         if order is not None:
//             body["params"] = prepare_order_params(order)

//         endpoint = 'browse'
//         response = self._send_request(endpoint, body)
//         return parse_library_albums(
//             response,
//             lambda additionalParams: self._send_request(endpoint, body, additionalParams), limit)

//     def get_library_artists(self, limit: int = 25, order: str = None) -> List[Dict]:
//         """
//         Gets the artists of the songs in the user's library.

//         :param limit: Number of artists to return
//         :param order: Order of artists to return. Allowed values: 'a_to_z', 'z_to_a', 'recently_added'. Default: Default order.
//         :return: List of artists.

//         Each item is in the following format::

//             {
//               "browseId": "UCxEqaQWosMHaTih-tgzDqug",
//               "artist": "WildVibes",
//               "subscribers": "2.91K",
//               "thumbnails": [...]
//             }
//         """
//         self._check_auth()
//         body = {'browseId': 'FEmusic_library_corpus_track_artists'}
//         validate_order_parameter(order)
//         if order is not None:
//             body["params"] = prepare_order_params(order)
//         endpoint = 'browse'
//         response = self._send_request(endpoint, body)
//         return parse_library_artists(
//             response,
//             lambda additionalParams: self._send_request(endpoint, body, additionalParams), limit)

//     def get_library_subscriptions(self, limit: int = 25, order: str = None) -> List[Dict]:
//         """
//         Gets the artists the user has subscribed to.

//         :param limit: Number of artists to return
//         :param order: Order of artists to return. Allowed values: 'a_to_z', 'z_to_a', 'recently_added'. Default: Default order.
//         :return: List of artists. Same format as :py:func:`get_library_artists`
//         """
//         self._check_auth()
//         body = {'browseId': 'FEmusic_library_corpus_artists'}
//         validate_order_parameter(order)
//         if order is not None:
//             body["params"] = prepare_order_params(order)
//         endpoint = 'browse'
//         response = self._send_request(endpoint, body)
//         return parse_library_artists(
//             response,
//             lambda additionalParams: self._send_request(endpoint, body, additionalParams), limit)

//     def get_liked_songs(self, limit: int = 100) -> Dict:
//         """
//         Gets playlist items for the 'Liked Songs' playlist

//         :param limit: How many items to return. Default: 100
//         :return: List of playlistItem dictionaries. See :py:func:`get_playlist`
//         """
//         return self.get_playlist('LM', limit)

//     def get_history(self) -> List[Dict]:
//         """
//         Gets your play history in reverse chronological order

//         :return: List of playlistItems, see :py:func:`get_playlist`
//           The additional property ``played`` indicates when the playlistItem was played
//           The additional property ``feedbackToken`` can be used to remove items with :py:func:`remove_history_items`
//         """
//         self._check_auth()
//         body = {'browseId': 'FEmusic_history'}
//         endpoint = 'browse'
//         response = self._send_request(endpoint, body)
//         results = nav(response, SINGLE_COLUMN_TAB + SECTION_LIST)
//         songs = []
//         for content in results:
//             data = nav(content, MUSIC_SHELF + ['contents'], True)
//             if not data:
//                 error = nav(content, ['musicNotifierShelfRenderer'] + TITLE, True)
//                 raise Exception(error)
//             menu_entries = [[-1] + MENU_SERVICE + FEEDBACK_TOKEN]
//             songlist = parse_playlist_items(data, menu_entries)
//             for song in songlist:
//                 song['played'] = nav(content['musicShelfRenderer'], TITLE_TEXT)
//             songs.extend(songlist)

//         return songs

//     def add_history_item(self, song):
//         """
//         Add an item to the account's history using the playbackTracking URI
//         obtained from :py:func:`get_song`.

//         :param song: Dictionary as returned by :py:func:`get_song`
//         :return: Full response. response.status_code is 204 if successful
//         """
//         url = song["playbackTracking"]["videostatsPlaybackUrl"]["baseUrl"]
//         CPNA = ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_")
//         cpn = "".join((CPNA[randint(0, 256) & 63] for _ in range(0, 16)))
//         params = {"ver": 2, "c": "WEB_REMIX", "cpn": cpn}
//         return self._send_get_request(url, params)

//     def remove_history_items(self, feedbackTokens: List[str]) -> Dict:  # pragma: no cover
//         """
//         Remove an item from the account's history. This method does currently not work with brand accounts

//         :param feedbackTokens: Token to identify the item to remove, obtained from :py:func:`get_history`
//         :return: Full response
//         """
//         self._check_auth()
//         body = {'feedbackTokens': feedbackTokens}
//         endpoint = 'feedback'
//         response = self._send_request(endpoint, body)

//         return response

//     def rate_song(self, videoId: str, rating: str = 'INDIFFERENT') -> Dict:
//         """
//         Rates a song ("thumbs up"/"thumbs down" interactions on YouTube Music)

//         :param videoId: Video id
//         :param rating: One of 'LIKE', 'DISLIKE', 'INDIFFERENT'

//           | 'INDIFFERENT' removes the previous rating and assigns no rating

//         :return: Full response
//         """
//         self._check_auth()
//         body = {'target': {'videoId': videoId}}
//         endpoint = prepare_like_endpoint(rating)
//         if endpoint is None:
//             return

//         return self._send_request(endpoint, body)

//     def edit_song_library_status(self, feedbackTokens: List[str] = None) -> Dict:
//         """
//         Adds or removes a song from your library depending on the token provided.

//         :param feedbackTokens: List of feedbackTokens obtained from authenticated requests
//             to endpoints that return songs (i.e. :py:func:`get_album`)
//         :return: Full response
//         """
//         self._check_auth()
//         body = {'feedbackTokens': feedbackTokens}
//         endpoint = 'feedback'
//         return endpoint if not endpoint else self._send_request(endpoint, body)

//     def rate_playlist(self, playlistId: str, rating: str = 'INDIFFERENT') -> Dict:
//         """
//         Rates a playlist/album ("Add to library"/"Remove from library" interactions on YouTube Music)
//         You can also dislike a playlist/album, which has an effect on your recommendations

//         :param playlistId: Playlist id
//         :param rating: One of 'LIKE', 'DISLIKE', 'INDIFFERENT'

//           | 'INDIFFERENT' removes the playlist/album from the library

//         :return: Full response
//         """
//         self._check_auth()
//         body = {'target': {'playlistId': playlistId}}
//         endpoint = prepare_like_endpoint(rating)
//         return endpoint if not endpoint else self._send_request(endpoint, body)

  // def subscribe_artists(self, channelIds: List[str]) -> Dict:
  //     """
  //     Subscribe to artists. Adds the artists to your library

  //     :param channelIds: Artist channel ids
  //     :return: Full response
  //     """
  //     self._check_auth()
  //     body = {'channelIds': channelIds}
  //     endpoint = 'subscription/subscribe'
  //     return self._send_request(endpoint, body)

//     def unsubscribe_artists(self, channelIds: List[str]) -> Dict:
//         """
//         Unsubscribe from artists. Removes the artists from your library

//         :param channelIds: Artist channel ids
//         :return: Full response
//         """
//         self._check_auth()
//         body = {'channelIds': channelIds}
//         endpoint = 'subscription/unsubscribe'
//         return self._send_request(endpoint, body)
}
